#pragma once 

//在模拟实现string完整类之前,我们必须先了解深浅拷贝
//我们自己写string类的时候,如果使用系统默认生成的拷贝构造函数(浅拷贝)
//假如是string s2(s1);
//那么默认的拷贝构造函数,就会把s1._str直接复制给s2._str,那么s2和s1就会指向同一个目标对象,最后对这块空间就会释放两次,从而报错
//所以就需要我们自己实现拷贝构造函数(深拷贝)
//首先new一块空间出来,然后使用strcpy将字符串的内容进行拷贝
//这样的s1和s2就是分别的两块空间,但是内容是一样的
//这里实现的代码也可以简单的看作是实现了一个简单的string类(不考虑增删查改)

namespace qds
{
    class string 
    {
public:
    //深拷贝传统写法 - 本本分分去完成深拷贝
    string(const char* str)
        :_str(new char[strlen(str) + 1])
    {
        strcpy(_str, str);
    }
        
    //string(const string& s)
    //    :_str(new char[strlen(s._str) + 1])
    //{
    //    strcpy(_str, s._str);
    //}
    //
    //string& operator=(const string& s)
    //{
    //    //错误写法
    //    //if (this != &s)
    //    //{
    //        ////delete基本是可以成功的,可是new有时候会失败
    //        ////下面这种写法可能会导致,_str释放完空间后,new失败,
    //        ////没有赋值成功,但是连原来的字符串都没了
    //        //delete[] _str;
    //        //_str = new char[strlen(s._str + 1)];
    //        //strcpy(_str, s._str);
    //    //}
    //    //return *this;

    //    //正确写法
    //    if (this != &s)
    //    {
    //        //这种写法是优先申请的空间,如果申请失败,也不会影响_str
    //        //申请成功之后,在进行delete_str
    //        ////这样就算new失败了也不会影响原来的字符串
    //        char* tmp = new char(strlen(s._str + 1));
    //        strcpy(tmp, s._str);
    //        delete[] _str;
    //        _str = tmp;
    //    }
    //    return *this;
    //}

    //深拷贝现代写法 - 投机取巧去完成深拷贝
    string(const string& s)
        :_str(nullptr)
         //这里需要初始化_str为nullptr的原因是,默认的_str是随机值
         //下面进行了指针的交换后,最后的tmp出了作用域就会进行执行析构函数
         //从而释放随机值,就会报错,而释放空指针不会报错
    {
        //首先复用已经写好的构造函数
        string tmp(s._str);
        //将两个string类的_str指针进行交换,从而实现拷贝
        swap(_str, tmp._str);
    }

    //正常写法
   // string& operator=(const string& s)
   // {
   //     if (this != &s)
   //     {
   //         //继续把刚刚实现好的拷贝构造函数复用
   //         string tmp(s);
   //         //交换了指针后,tmp出了作用域,调用析构函数,顺便把原来的空间也给销毁了
   //         swap(_str, tmp._str);
   //     }
   //     return *this;
   // }
    
    //简洁写法
    //这里不用考虑传入的对象是const string的情况,因为这里的形参是调用的拷贝构造函数,传入的是const string的对象也会构造出string的对象
    //简洁写法和正常写法相比少了个优势,就是不能检查是否是自己对自己进行赋值
    string& operator=(string s)
    {
        swap(_str, s._str);
        return *this;
    }

    ~string()
    {
        delete[] _str;
        _str = nullptr;
    }

    const char* ret_string() const
    {
        return _str;
    }

private:
        char* _str;

    };

    void test()
    {
        const string s1("我的世界");
        cout << s1.ret_string() << endl;
         string s2(s1);
        cout << s2.ret_string() << endl;
        string s3("刘坤233");
        s3 = s1;
        cout << s3.ret_string() << endl;
    }
}


